package java核心及工具类;
/*
Optional类的介绍
javadoc中的介绍
这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true，调用get()方法会返回> 该对象。

使用场景
用于避免处理空指针异常（NullPointerException)。将可能为空值的变量放入Optional容器中，通过Optional容器访问对象.

Optional类包含的方法介绍及其示例:

	
1.《of》
为非null的值创建一个Optional。

of方法通过工厂方法创建Optional类。需要注意的是，创建对象时传入的参数不能为null。如果传入参数为null，则抛出NullPointerException 。

//调用工厂方法创建Optional实例
Optional<String> name = Optional.of("Sanaulla");
//传入参数为null，抛出NullPointerException.
Optional<String> someNull = Optional.of(null);


2.《ofNullable》
为指定的值创建一个Optional，如果指定的值为null，则返回一个空的Optional。

ofNullable与of方法相似，唯一的区别是可以接受参数为null的情况。示例如下：

//下面创建了一个不包含任何值的Optional实例
//例如，值为'null'
Optional empty = Optional.ofNullable(null);

public static <T> Optional<T> ofNullable(T value) {
        return value == null ? (Optional<T>) EMPTY : new Optional<>(value);
    }

3.《isPresent》
非常容易理解：如果值存在返回true，否则返回false。判断是否包换对象；

类似下面的代码：
//isPresent方法用来检查Optional实例中是否包含值
if (name.isPresent()) {
  //在Optional实例内调用get()返回已存在的值
  System.out.println(name.get());//输出Sanaulla
}


4.《get》
如果Optional有值则将其返回，否则抛出NoSuchElementException。

上面的示例中，get方法用来得到Optional实例中的值。下面我们看一个抛出NoSuchElementException的例子：

//执行下面的代码会输出：No value present 
try {
  //在空的Optional实例上调用get()，抛出NoSuchElementException
  System.out.println(empty.get());
} catch (NoSuchElementException ex) {
  System.out.println(ex.getMessage());
}


5.《ifPresent》
如果Optional实例有值则为其调用consumer，否则不做处理
void ifPresent(Consumer<? super T> consumer)：如果有值，就执行 Consumer 接口的实现代码，并且该值会作为参数传递给它；

要理解ifPresent方法，首先需要了解Consumer类。简答地说，Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理，但没有返回
值。Java8支持不用接口直接通过lambda表达式传入参数。

如果Optional实例有值，调用ifPresent()可以接受接口段或lambda表达式。类似下面的代码：

//ifPresent方法接受lambda表达式作为参数。
//lambda表达式对Optional的值调用consumer进行处理。
name.ifPresent((value) -> {
  System.out.println("The length of the value is: " + value.length());
});

6.《orElse》
如果有值则将其返回，否则返回指定的其它值。
public T orElse(T other) {
        return value != null ? value : other;
    }

T orElse(T other)：如果有值则将其返回，否则返回指定的other 对象；
orElse() 方法用于返回包裹在 Optional 对象中的值，如果该值不为 null，则返回；否则返回默认值。该方法的参数类型和值的类型一致。
如果Optional实例有值则将其返回，否则返回orElse方法传入的参数。示例如下：

//如果值不为null，orElse方法返回Optional实例的值。
//如果为null，返回传入的消息。
//输出：There is no value present!
System.out.println(empty.orElse("There is no value present!"));
//输出：Sanaulla
System.out.println(name.orElse("There is some value!"));

String nullName = null;
String name = Optional.ofNullable(nullName).orElse("沉默王二");
System.out.println(name); // 输出：沉默王二


7.《orElseGet》
orElseGet与orElse方法类似，区别在于得到的默认值。orElse方法将传入的字符串作为默认值，orElseGet方法可以接受Supplier接口
的实现用来生成默认值。示例如下：
T orElseGet(Supplier<? extends T other>)：如果有值则将其返回，否则返回由Supplier接口实现提供的对象；
//orElseGet与orElse方法类似，区别在于orElse传入的是默认值，
//orElseGet可以接受一个lambda表达式生成默认值。
//输出：Default Value
System.out.println(empty.orElseGet(() -> "Default Value"));
//输出：Sanaulla
System.out.println(name.orElseGet(() -> "Default Value"));


8.《orElseThrow》
如果有值则将其返回，否则抛出supplier接口创建的异常。
T orElseThrow(Supplier<? extends X> exceptionSupplier)：如果有值则将其返回，否则抛出由Supplier接口实现提供的异常。
在orElseGet方法中，我们传入一个Supplier接口。然而，在orElseThrow中我们可以传入一个lambda表达式或方法，如果值不存在来抛出异常。示例如下：

try {
  //orElseThrow与orElse方法类似。与返回默认值不同，
  //orElseThrow会抛出lambda表达式或方法生成的异常 

  empty.orElseThrow(ValueAbsentException::new);
} catch (Throwable ex) {
  //输出: No value present in the Optional instance
  System.out.println(ex.getMessage());
}
ValueAbsentException定义如下：

class ValueAbsentException extends Throwable {

  public ValueAbsentException() {
    super();
  }

  public ValueAbsentException(String msg) {
    super(msg);
  }

  @Override
  public String getMessage() {
    return "No value present in the Optional instance";
  }
}


9.《map》
map方法文档说明如下：

如果有值，则对其执行调用mapping函数得到返回值。如果返回值不为null，则创建包含mapping返回值的Optional作为map方法返回值，否则返回空Optional。

map方法用来对Optional实例的值执行一系列操作。通过一组实现了Function接口的lambda表达式传入操作。map方法示例如下：

//map方法执行传入的lambda表达式参数对Optional实例的值进行修改。
//为lambda表达式的返回值创建新的Optional实例作为map方法的返回值。
Optional<String> upperName = name.map((value) -> value.toUpperCase());
System.out.println(upperName.orElse("No value found"));


10.《flatMap》
如果有值，为其执行mapping函数返回Optional类型返回值，否则返回空Optional。flatMap与map（Funtion）方法类似，区别在
于flatMap中的mapper返回值必须是Optional。调用结束时，flatMap不会对结果用Optional封装。

flatMap方法与map方法类似，区别在于mapping函数的返回值不同。map方法的mapping函数返回值可以是任何类型T，而flatMap方法
的mapping函数必须是Optional。

参照map函数，使用flatMap重写的示例如下：

//flatMap与map（Function）非常类似，区别在于传入方法的lambda表达式的返回类型。
//map方法中的lambda表达式返回值可以是任意类型，在map函数返回之前会包装为Optional。 
//但flatMap方法中的lambda表达式返回值必须是Optionl实例。 
upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));//输出SANAULLA


11.《filter》
filter个方法通过传入限定条件对Optional实例的值进行过滤。文档描述如下：

如果有值并且满足断言条件返回包含该值的Optional，否则返回空Optional。

读到这里，可能你已经知道如何为filter方法传入一段代码。是的，这里可以传入一个lambda表达式。对于filter函数我们应该传入实现
了Predicate接口的lambda表达式。如果你不熟悉Predicate接口，可以参考这篇文章。

现在我来看看filter的各种用法，下面的示例介绍了满足限定条件和不满足两种情况：

//filter方法检查给定的Option值是否满足某些条件。
//如果满足则返回同一个Option实例，否则返回空Optional。
Optional<String> longName = name.filter((value) -> value.length() > 6);
System.out.println(longName.orElse("The name is less than 6 characters"));//输出Sanaulla

//另一个例子是Optional值不满足filter指定的条件。
Optional<String> anotherName = Optional.of("Sana");
Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
//输出：name长度不足6字符
System.out.println(shortName.orElse("The name is less than 6 characters"));

一个完整的示例对用法集中展示：
*/
/*
public class 使用Optional1 {

  public static void main(String[] args) {
    //创建Optional实例，也可以通过方法返回值得到。
    Optional<String> name = Optional.of("Sanaulla");

    //创建没有值的Optional实例，例如值为'null'
    Optional empty = Optional.ofNullable(null);

    //isPresent方法用来检查Optional实例是否有值。
    if (name.isPresent()) {
      //调用get()返回Optional值。
      System.out.println("1.name.get():  "+name.get());
    }

    try {
      //在Optional实例上调用get()抛出NoSuchElementException。
      System.out.println(empty.get());
    } catch (NoSuchElementException ex) {
      System.out.println("2.empty.get(): "+ex.getMessage());
    }

    //ifPresent方法接受lambda表达式参数。
    //如果Optional值不为空，lambda表达式会处理并在其上执行操作。(接受name.get(),即value="Sanaulla")
    name.ifPresent((value) -> {//void java.util.Optional.ifPresent(Consumer<? super String> action)
      System.out.println("3.value.length(): "+"The length of the value is: " + value.length()+value);
    });

    //如果有值orElse方法会返回Optional实例，否则返回传入的错误信息。
    //T java.util.Optional.orElse(T other)  return value != null ? value : other;
    System.out.println("4.empty.orElse: "+empty.orElse("There is no value present!"));
    System.out.println("5.name.orElse: "+name.orElse("There is some value!"));

    //orElseGet与orElse类似，区别在于传入的默认值。
    //orElseGet接受lambda表达式生成默认值。
    //T java.util.Optional.orElseGet(Supplier<? extends T> supplier) return value != null ? value : supplier.get();
    System.out.println("6.empty.orElseGet: "+empty.orElseGet(() -> "Default Value"));
    System.out.println("7.name.orElseGet: "+name.orElseGet(() -> "Default Value"));

    try {
      //orElseThrow与orElse方法类似，区别在于返回值。
      //orElseThrow抛出由传入的lambda表达式/方法生成异常。
      empty.orElseThrow(Exception::new);
//      empty.orElseThrow(ValueAbsentException::new);
    } catch (Throwable ex) {
      System.out.println(ex.getMessage());
    }

    //map方法通过传入的lambda表达式修改Optonal实例默认值。 
    //lambda表达式返回值会包装为Optional实例。
    //<String> Optional<String> java.util.Optional.map(Function<? super String, ? extends String> mapper)
    Optional<String> upperName = name.map((value) -> value.toUpperCase());
    System.out.println(upperName.orElse("No value found"));

    //flatMap与map（Funtion）非常相似，区别在于lambda表达式的返回值。
    //map方法的lambda表达式返回值可以是任何类型，但是返回值会包装成Optional实例。
    //但是flatMap方法的lambda返回值总是Optional类型。
    upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
    System.out.println(upperName.orElse("No value found"));

    //filter方法检查Optiona值是否满足给定条件。
    //如果满足返回Optional实例值，否则返回空Optional。
    Optional<String> longName = name.filter((value) -> value.length() > 6);
    System.out.println(longName.orElse("The name is less than 6 characters"));

    //另一个示例，Optional值不满足给定条件。
    Optional<String> anotherName = Optional.of("Sana");
    Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
    System.out.println(shortName.orElse("The name is less than 6 characters"));

  }
}
*/
/*
请查看如下实例，其在运行时会发生空指针异常：

实例演示
import java.util.Optional;

public class OptionalDemo2 {

    static class Category {
        private String name;

        public Category(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Category{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

    static class Goods {
        private String name;

        private Category category;

        public Goods() {

        }

        public Goods(String name, Category category) {
            this.name = name;
            this.category = category;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Category getCategory() {
            return category;
        }

        public void setCategory(Category category) {
            this.category = category;
        }

        @Override
        public String toString() {
            return "Good{" +
                    "name='" + name + '\'' +
                    ", category=" + category +
                    '}';
        }
    }

//    
//    获取商品的分类名称
//    @param goods 商品
//    @return 分类名称
//    
    static String getGoodsCategoryName(Goods goods) {
        return goods.getCategory().getName();
    }

    public static void main(String[] args) {
        // 实例化一个商品类
        Goods goods = new Goods();
        // 获取商品的分类名称
        String categoryName = getGoodsCategoryName(goods);
        System.out.println(categoryName);
    }
}
*/
/*
运行案例
点击 "运行案例" 可查看在线运行效果
运行结果：

实例中，由于在实例化Goods类时，我们没有给其下面的Category类型的属性category赋值，它就为 null，在运行时， null.getName()就会抛出空指针异常。
同理，如果goods实例为null，那么null.getCategory()也会抛出空指针异常。

在没有使用Optional类的情况下，想要优化代码，就不得不改写getGoodsCategoryName()方法：

这也就是我们上面说的null检查逻辑代码，此处有两层if嵌套，如果有更深层次的级联属性，就要嵌套更多的层级。

下面我们将Optional类引入实例代码：

实例演示
*/

import java.util.Optional;
import java.util.function.Predicate;

public class 使用Optional {

    static class Category {
        private String name;

        public Category(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Category{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

    static class Goods {
        private String name;

        private Category category;

        public Goods() {

        }

        public Goods(String name, Category category) {
            this.name = name;
            this.category = category;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Category getCategory() {
            return category;
        }

        public void setCategory(Category category) {
            this.category = category;
        }

        @Override
        public String toString() {
            return "Good{" +
                    "name='" + name + '\'' +
                    ", category=" + category +
                    '}';
        }
    }

//     获取商品的分类名称（使用 Optional 类包装）
//     @param goods 商品
//     @return 分类名称
    static String getGoodsCategoryName(Goods goods) {
        // 将商品实例包装入 Optional 类，创建 Optional<Goods> 对象
        Optional<Goods> goodsOptional = Optional.ofNullable(goods);//null不报异常
        Goods goods1 = goodsOptional.orElse(new Goods("默认商品", new Category("默认分类--")));//如果null,则取默认值
        // 此时 goods1 一定是非空，不会产生空指针异常
        Category category = goods1.getCategory();

        // 将分类实例包装入 Optional 类，创建 Optional<Category> 对象
        Optional<Category> categoryOptional = Optional.ofNullable(category);//null不报异常
        Category category1 = categoryOptional.orElse(new Category("默认分类===="));//如果null,则取默认值
        // 此时 category1 一定是非空，不会产生空指针异常
        return category1.getName();
    }

    public static void main(String[] args) {
        // 实例化一个商品类
        Goods goods = null;
        // 获取商品的分类名称
        String categoryName = getGoodsCategoryName(goods);
        System.out.println(categoryName);
        
        String name = "aaaa";
        Optional<String> optOrNull = Optional.ofNullable(name);
        System.out.println(optOrNull); // 输出：Optional.empty

        Optional<String> opt = Optional.of("沉默王二");
        opt.ifPresent(str -> System.out.println(str.length()));//void java.util.Optional.ifPresent(Consumer<? super String> action)

        String nullName = null;
        String name1 = Optional.ofNullable(nullName).orElse("沉默王二");
        System.out.println(name1); // 输出：沉默王二
        
        //map() 方法与 filter() 方法结合起来用，前者用于将密码转化为小写，后者用于判断长度以及是否是“password”。
        String password = "password";
        Optional<String>  opt1 = Optional.ofNullable(password);

        Predicate<String> len6 = pwd -> pwd.length() > 6;
        Predicate<String> len10 = pwd -> pwd.length() < 10;
        Predicate<String> eq = pwd -> pwd.equals("password");

        
        System.out.println(opt1.map(String::toUpperCase).get());//PASSWORD
        boolean result = opt1.map(String::toLowerCase).filter(len6.and(len10 ).and(eq)).isPresent();
        System.out.println(result);//true

        
    }
}
/*
运行案例
点击 "运行案例" 可查看在线运行效果
运行结果：*/

